package com.seednia.module.core.profile.service;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.codehaus.jackson.map.ObjectMapper;

import com.seednia.foundation.framework.dto.AbstractRecord;
import com.seednia.foundation.framework.dto.QueryParams;
import com.seednia.foundation.framework.service.RequestContext;
import com.seednia.foundation.framework.service.RequestContextFactory;
import com.seednia.foundation.framework.service.ServiceContainer;
import com.seednia.foundation.framework.service.ServiceException;
import com.seednia.module.core.category.dto.CategoryCriteria;
import com.seednia.module.core.category.dto.CategoryDto;
import com.seednia.module.core.category.service.ICategoryService;
import com.seednia.module.core.profile.dto.ExperienceDto;
import com.seednia.module.core.profile.dto.ProfileAttributeDto;
import com.seednia.module.core.profile.dto.ProfileDto;
import com.seednia.module.core.user.dto.UserDto;

/**
 * Imports Facebook graph's (as per 2012/06/11) user information
 * and creates Seednia's User Profile
 * 
 * @author Young Suk Ahn
 */
public class FacebookUserImporter {
	
	private HashMap<String, CategoryDto> categoryMap = new HashMap<String, CategoryDto> ();
	
	
	public String getFormat()
	{
		return "FB";
	}
	
	public void init()
	{
		ICategoryService categorySvc = ServiceContainer.getInstance().getByType(ICategoryService.class);
		
		RequestContext requestContext = RequestContextFactory.createSystemRequestContext();
		CategoryCriteria criteria = new CategoryCriteria(); 
		try {
			List<CategoryDto> categories = categorySvc.getCategoryList(requestContext, criteria, new QueryParams(0, Integer.MAX_VALUE));
			
			for (CategoryDto category: categories ) {
				if (category.getType() == null) {
					category.setType("");
				} 
				categoryMap.put(category.getKind()+":"+category.getType()+":"+category.getName(), category);
			}
		} catch (ServiceException e) {
			e.printStackTrace();
		}
	}
	
	public AbstractRecord importSource(String input) throws IOException
	{
		ProfileDto retval = new ProfileDto();
		importSource(retval, input); 
		return retval;
	}
	
	public void importSource(AbstractRecord source, String input) throws IOException
	{
		ProfileDto record = (ProfileDto)source;
		if (record.getCreatorDto() == null)
			record.setCreatorDto(new UserDto());
		
		ObjectMapper jsonObjMapper = new ObjectMapper();
		
		Map<String, Object> profileMap = jsonObjMapper.readValue(input, Map.class);
		
		//retval.setBirthDate(birthDate);
		record.setCreateDt(new Date());

		if (profileMap.containsKey("id"))  // Facebook's ID
		{
			//retval.set
		} else {
			throw new IllegalArgumentException("Missing field 'id' ");
		}
		
		if (profileMap.containsKey("name"))
		{
			record.getCreatorDto().setLcName( (String)profileMap.get("name") );
		} else {
			throw new IllegalArgumentException("Missing field 'name' ");
		}
		
		if (profileMap.containsKey("first_name"))
		{
			record.getCreatorDto().setFirstName( (String)profileMap.get("first_name") );
		} else {
			throw new IllegalArgumentException("Missing field 'first_name' ");
		}
		if (profileMap.containsKey("middle_name"))
		{
			record.getCreatorDto().setMiddleName( (String)profileMap.get("middle_name") );
		}
		if (profileMap.containsKey("last_name"))
		{
			record.getCreatorDto().setLastName( (String)profileMap.get("last_name") );
		} else {
			throw new IllegalArgumentException("Missing field 'last_name' ");
		}		
		if (profileMap.containsKey("link"))
		{
			ProfileAttributeDto websiteEntry = createAttributeDto(
					getCategory("ProfileAttribute", "Website", "Social")
					, "Facebook"
					, (String)profileMap.get("link") );
			record.addWebsiteDto(websiteEntry);
		}	
		if (profileMap.containsKey("username")) // Facebook Username
		{
			ProfileAttributeDto contact = new ProfileAttributeDto();
			contact.setCategoryDto( getCategory("ProfileAttribute", "Contact", "Social") );
			contact.setName("Facebook");
			contact.setValue((String)profileMap.get("username") );
			record.addWebsiteDto(contact);
		} 	
		if (profileMap.containsKey("hometown"))
		{
			record.setHometown( getNodeName(profileMap, "hometown") );
		}
		if (profileMap.containsKey("location"))
		{
			record.setCurrentLocation( getNodeName(profileMap, "location") );
		}
		if (profileMap.containsKey("bio"))
		{
			record.setHighlight( (String)profileMap.get("bio") );
		}
		if (profileMap.containsKey("quotes"))
		{
			record.setPhilosophy( (String)profileMap.get("quotes") );
		}
		if (profileMap.containsKey("gender"))
		{
			record.setGender( (String)profileMap.get("gender") );
			if (record.getGender().length() > 2) {
				record.setGender( record.getGender().substring(0, 1) );
			}
			
		}
		if (profileMap.containsKey("website"))
		{
			String[] websites = ((String)profileMap.get("website")).split("[\\r\\n]+") ;
			
			for (String website: websites) {
				ProfileAttributeDto websiteEntry = createAttributeDto(
						getCategory("ProfileAttribute", "Website", "Other")
						, "Website"
						, website );
				record.addWebsiteDto(websiteEntry);
			}
		}
		if (profileMap.containsKey("timezone"))
		{
			record.setTimezone( (Integer)profileMap.get("timezone") );
		}
		if (profileMap.containsKey("locale"))
		{
			record.setLocale( (String)profileMap.get("locale") );
		}
		
		
		if (profileMap.containsKey("work"))
		{
			processWork(record, profileMap.get("work"));
		}
		
		if (profileMap.containsKey("education"))
		{
			processEducation(record, profileMap.get("education"));
		}
		if (profileMap.containsKey("languages"))
		{
			processLanguages(record, profileMap.get("languages"));
		}
		
	}
	
	public void processWork(ProfileDto profileDto, Object workObj)
	{
		//profileDto
		if ( !(workObj instanceof List) )
			return;
		
		List<Object> workList = (List<Object>)workObj;
		
		//CategoryDto professional = getCategory("Experience","Academic","Education");
		CategoryDto professional = getCategory("Experience","Professional","Work");
		
		for (Object workNode: workList) {
			if ( !(workNode instanceof Map))
				return;
			Map<String, Object>workMap = (Map<String, Object>)workNode;
			
			ExperienceDto experience = new ExperienceDto();
			experience.setCategoryDto(professional);
			experience.setProfileSid(profileDto.getSid());
			
			if (workMap.containsKey("employer"))
			{
				experience.setOrganizationName( getNodeName(workMap, "employer") );
			}
			if (workMap.containsKey("location"))
			{
				experience.setLocation( getNodeName(workMap, "location") );
			}
			if (workMap.containsKey("position"))
			{
				experience.setTitle( getNodeName(workMap, "position") );
			}
			if (workMap.containsKey("start_date"))
			{
				// All dates are in ISO format, remove '-'
				experience.setStartDate( (String)workMap.get("start_date") );
			}
			if (workMap.containsKey("end_date"))
			{
				// All dates are in ISO format, remove '-'
				experience.setEndDate( (String)workMap.get("end_date") );
			}
			
			if (workMap.containsKey("projects")) {
				// process projects
			}
			profileDto.addExperienceDto(experience);
		}
	}

	public void processEducation(ProfileDto profileDto, Object educationObj)
	{
		
		if ( !(educationObj instanceof List) )
			return;
		
		List<Object> educationList = (List<Object>)educationObj;
		
		//CategoryDto professional = getCategory("Experience","Academic","Education");
		CategoryDto professional = getCategory("Experience","Professional","Work");
		
		for (Object eduNode: educationList) {
			if ( !(eduNode instanceof Map))
				return;
			Map<String, Object>eduMap = (Map<String, Object>)eduNode;
			
			ExperienceDto experience = new ExperienceDto();
			experience.setCategoryDto(professional);
			experience.setProfileSid(profileDto.getSid());
			
			if (eduMap.containsKey("school"))
			{
				
				experience.setOrganizationName( getNodeName(eduMap, "school") );
			}
			if (eduMap.containsKey("degree"))
			{
				experience.setTitle( getNodeName(eduMap, "degree") );
			}
			if (eduMap.containsKey("concentration"))
			{
				List<String> concentrations = getNodeNames(eduMap, "concentration");
				if (concentrations.size() > 0)
					experience.setDepartment( concentrations.get(0) );
			}
			if (eduMap.containsKey("start_date"))
			{
				// All dates are in ISO format, remove '-'
				experience.setStartDate( (String)eduMap.get("start_date") );
			}
			if (eduMap.containsKey("year"))
			{
				// All dates are in ISO format, remove '-'
				experience.setEndDate( getNodeName(eduMap, "year") );
			}
			
			if (eduMap.containsKey("type"))
			{
				// Type: High school, College, graduate school
				experience.setType( (String)eduMap.get("type") );
			}
			
			profileDto.addEducationDto(experience);
		}
	}

	public void processLanguages(ProfileDto profileDto, Object languageObj)
	{
		//profileDto
		if ( !(languageObj instanceof List) )
			return;
		
		List<Object> languageList = (List<Object>)languageObj;
		
		//CategoryDto professional = getCategory("Experience","Professional","Work");
		
		for (Object langNode: languageList) {
			if ( !(langNode instanceof Map))
				return;
			Map<String, Object>langMap = (Map<String, Object>)langNode;
		}
	}
	
	public CategoryDto getCategory(String kind, String type, String name)
	{
		if (type == null) {
			type = "";
		}
		CategoryDto retval = categoryMap.get(kind+":"+type+":"+name);
		return retval;
	}
	
	public ProfileAttributeDto createAttributeDto(CategoryDto category, String name, String value)
	{
		ProfileAttributeDto profileAttribute = new ProfileAttributeDto();
		profileAttribute.setCategoryDto( category );
		profileAttribute.setName(name);
		profileAttribute.setValue(value );
		return profileAttribute; 
	}
	
	public String getNodeName(Map<String, Object> map, String key) {
		Map<String, Object> node = (Map<String, Object>)map.get(key);
		return (String)node.get("name")  ;
	}
	
	public ArrayList<String> getNodeNames(Map<String, Object> map, String key) {
		List<Object> nodeList = (List<Object>)map.get(key);
		ArrayList<String> retval = new ArrayList<String>();
		for(Object obj: nodeList) {
			Map<String, Object> node = (Map<String, Object>)obj;
			retval.add( (String)node.get("name") ); 
		}
		return retval ;
	}
}
